#pragma once
#include <thread>
#include <future>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <functional>
#include <atomic>
#include <iostream>
class ThreadPool{
public:
    using ptr = std::shared_ptr<ThreadPool>;
    using functor = std::function<void(void)>;
    ThreadPool(int thread_num = 1): _stop(false){
        for(int i = 0; i < thread_num; i++){
            _threads.emplace_back(&ThreadPool::entry, this);
        }
    }
    ~ThreadPool(){
        stop();
    }

    // 向任务队列中插入任务
    // 处理函数未知，参数未知，返回值未知
    // 1、将函数用 bind 包装成无参的函数
    // 2、返回值类型用 decltype 进行动态类型推导
    // 3、用它创建 package_task 对象
    // 4、在用 lambda 表达式对 package_task 对象进行回调，就能正确的运行任务函数
    template<class F, class ...Args>
    auto push(F&& func, Args&& ...args) -> std::future<decltype(func(args...))>{
        using return_type = decltype(func(args...));
        auto fun = std::bind(std::forward<F>(func), std::forward<Args>(args)...);
        auto ptask = std::make_shared<std::packaged_task<return_type()>>(fun);
        std::future<return_type> fu = ptask->get_future();
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _tasks.push_back( [ptask](){ (*ptask)(); } );
            _cv.notify_one();
        }
        return fu;
    }
    void stop(){
        if(_stop) return;
        _stop = true;
        _cv.notify_all(); // 如果线程池结束，就唤醒所有线程，让它们终止
        for(auto& th:_threads){
            th.join();
        }
    }
private:
    // 线程执行的函数
    void entry(){
        while(!_stop)
        {
            std::vector<functor> tmp;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                // 只有当线程池停止 / 有任务时才执行
                _cv.wait(lock, [this]()->bool{ return _stop || !_tasks.empty();});
                std::swap(tmp, _tasks);
            }
            for(auto t : tmp){
                t();
            }
        }
    }
private:
    std::atomic<bool> _stop; // 原子操作
    std::mutex _mutex; // 锁
    std::condition_variable _cv; // 条件变量
    std::vector<functor> _tasks; // 任务池
    std::vector<std::thread> _threads; // 线程池
};
